Skip to content

refactor: single-owner edge geometry pipeline + layout orchestrator - #53

Open
RhizoNymph wants to merge 10 commits into
mainfrom
refactor/viz-integration
Open

refactor: single-owner edge geometry pipeline + layout orchestrator#53
RhizoNymph wants to merge 10 commits into
mainfrom
refactor/viz-integration

Conversation

@RhizoNymph

Copy link
Copy Markdown
Owner

Integrates two refactor branches of the visualization layer (refactor/edge-geometry-pipeline, refactor/layout-orchestrator), merged and seam-tested here. One PR, both halves reviewable via their own commit series.

Why

Edge geometry had no single owner: an edge's polyline was mutated by ~6 loosely-coupled stages across three files, endpoint anchors were stored but never read (every consumer re-inferred them from geometry with three different tolerance heuristics), hit-testing measured a polyline that was not the one drawn, and PixiRenderer carried an untestable layout-orchestration state machine layering three staleness mechanisms.

What changed

Edge geometry (refactor/edge-geometry-pipeline)

  • Anchors are now the durable contract: decided exactly once at layout time from the pristine route (edgeAnchorAtBoundary, exact segment-direction reading instead of tolerance ladders) and consumed by every later stage. Deleted inferEdgeAnchor/inferAnchorSide/inferApproachSide/inferAnchorsFromPolyline/inferPointSide.
  • New layout/edgeRoutePipeline.ts: the draw-time stage order is one function body — anchorEndpoints -> spreadEndpointLanes -> detourAroundObstacles, each a pure stage returning new records; a stage that moves an endpoint emits its updated anchor. edgeDrawing.ts is left with layer management and stroking.
  • anchorEdgePolyline reports { points, rerouted } instead of silently discarding ELK's route; surfaced as RoutedEdge.origin.
  • layout/routingConstants.ts owns every routing tolerance/margin, with derived values expressed as arithmetic (OBSTACLE_QUERY_MARGIN = NODE_OBSTACLE_MARGIN + DETOUR_GUTTER + allowance) so they cannot drift. Hot-loop constants are re-bound as module-local consts in edgeGeometry.ts (V8 does not constant-fold live imported bindings; an interleaved A/B run isolated ~8% routing cost before the re-bind).
  • Hit-testing (hover, tooltip, double-click drill-in) now measures the polyline that was actually drawn, falling back to the layout route before first draw. This is the one deliberate behavior fix: after lane spreading, obstacle detours or node drags, the old code tested a line that was not on screen.
  • edgeRoutingBudget.ts moved renderers/ -> layout/ (it is a layout-side dependency); extractLayout split into pure ELK-free functions (layout/elkExtract.ts).

Layout orchestration (refactor/layout-orchestrator)

  • New layout/layoutOrchestrator.ts owns the layout-request lifecycle previously inlined in PixiRenderer: the run-latest coalescing queue, the request-id stale guard, the pending gate on the cheap visibility redraw, and the latest-vs-applied visible-set reconciliation. All side effects injected, so the invariants are unit-tested with fakes (burst coalescing, stale-result discard, mid-flight visibility reconciliation, error path releasing the gate, edges-phase no-op before first layout).
  • PixiRenderer supplies effects and keeps only rendering; the four dead re-export shims (NodeRenderer, EdgeRenderer, LabelRenderer, interactionManager) are deleted.

Integration (this branch)

  • tests/vizIntegrationSeam.test.ts wires the real geometry chain in as the orchestrator's effects and pins the composed invariants neither half tests alone: every edge phase receives the newest APPLIED layout (including a previous edge phase's result) as its input, routed polylines survive edge-phase reruns through that adoption, and anchors and geometry still agree after the full draw-time pipeline (including lane-moved endpoints). Both docs' merge conflicts resolved by combining the halves.

Edge-routing benchmark is at parity with main (all scenarios within run variance; 800n/500e indexed routing 251ms vs 259ms on main). Visual output is unchanged apart from the hit-test fix and rare anchor-side differences where the old tolerance ladder guessed wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant